package org.springframework.xml.xpath.proxy;

import org.apache.log4j.Logger;
import org.springframework.util.Assert;
import org.springframework.xml.xpath.proxy.conversion.Converter;
import org.w3c.dom.Node;

import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

/**
 * Core component for creating xpath proxy instances.
 * Provided with an interface that is annotated with {@link XPathMethod} annotations, it can create proxy instances
 * for the Node provided to the {@link #createProxy(org.w3c.dom.Node)} method.
 *
 * @author Ray Krueger
 */
public class XPathProxyFactory<T> {

    private static final Logger log = Logger.getLogger(XPathProxyFactory.class);

    private final Class<T> targetInterface;
    private List<Converter> converters = new ArrayList<Converter>();

    public XPathProxyFactory(Class<T> targetInterface) {
        Assert.isTrue(targetInterface.isInterface(),
                "targetInterface constructor argument must be an interface");
        this.targetInterface = targetInterface;

    }

    public void registerConverter(Converter converter) {
        converters.add(converter);
    }

    public Converter[] getConverters() {
        return converters.toArray(new Converter[converters.size()]);
    }

    public Class<T> getTargetInterface() {
        return targetInterface;
    }

    /**
     * Create a proxy of the interface given in during instantation of this factory around the Node given here.
     *
     * @param node The node our {@link XPathMethod} annotation values will be executed against.
     * @return The value returned by that xpath query, possibly after conversion by any {@link Converter}s used
     */
    @SuppressWarnings("unchecked")
    public T createProxy(Node node) {
        log.debug("Creating proxy for [" + targetInterface.getName() + "]");

        return (T) Proxy.newProxyInstance(
                targetInterface.getClassLoader(),
                new Class[]{targetInterface},
                newInvocationHandler(node));
    }

    protected XPathMethodInvocationHandler newInvocationHandler(Node element) {
        return new XPathMethodInvocationHandler(getTargetInterface(), element, getConverters());
    }

}
